package org.road0329.tools.proto_parse_tool.ems;

import org.road0329.tools.proto_parse_tool.ems.dataobject.DataWaterExtend;
import org.road0329.tools.proto_parse_tool.ems.dataobject.PointPowerStatus;
import org.road0329.tools.proto_parse_tool.ems.dataobject.wmc.*;
import org.road0329.tools.proto_parse_tool.enums.WaterMeterCollectorRespTypeEnum;
import org.road0329.tools.proto_parse_tool.utils.DateUtil;
import org.road0329.tools.proto_parse_tool.utils.ProtocolUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by Administrator on 2018-09-26.
 */
@Component
public class ProtocolWaterMeterCollectorUtil {
    private static Logger logger = LoggerFactory.getLogger(ProtocolWaterMeterCollectorUtil.class);
    private static final short STATUS_OPEN = 0;//阀门 打开
    private static final short STATUS_CLOSE = 1;//阀门 关闭
    private static final short SEND_COMPLETE = 999;
    private static final int MAX_RETRY_COUNT = 3;

    private static byte[] validateMsg(byte[] msg, int index) {
        if (msg == null || msg.length <= 0) {
            logger.debug("msg is empty.");
            return null;
        }
        if (index + 4 > msg.length) {
            logger.debug("index:{} >= msg.length:{}.", index, msg.length);
            return null;
        }
        if ((msg[index] & 0xff) != 0x8b) {
            logger.debug("msg's header is illegal.");
            return null;
        }

        int len = msg.length;

        int msgLen = ProtocolUtils.byteToShort(msg[index + 1], msg[index + 2]);
        if (msgLen + 4 > len) {
            logger.debug("msg's length:{} != actual length:{}.", msgLen, len);
            return null;
        }
        if ((msg[msgLen + 3] & 0xff) != 0x88) {
            logger.debug("msg's tail is illegal.");
            return null;
        }

        byte[] checkSum = ProtocolUtils.checkSum2Bytes(msg, 1, msgLen);
        if (checkSum[0] != msg[msgLen + 1] || checkSum[1] != msg[msgLen + 2]) {
            logger.debug("calc checksum:{} != msg's checksum:{}.", ProtocolUtils.bytesToHexString(checkSum), ProtocolUtils.getByteToHexString(msg, msgLen, 2, ""));
            return null;
        }
        byte[] buf = new byte[msgLen + 4];
        System.arraycopy(msg, index, buf, 0, (msgLen + 4));
        return buf;
    }


    public static List<Object> parseMessage(byte[] msg) {

        int index = 0;
        //支持一包多帧报文
        byte[] buf = validateMsg(msg, index);
        List<Object> result = new ArrayList<>();
        while (buf != null) {
            index += buf.length;
            String respType = ProtocolUtils.getByteToHexString(buf, 3, 2, "");
            WaterMeterCollectorRespTypeEnum respTypeEnum = WaterMeterCollectorRespTypeEnum.getEnmuByValue(respType);
            if (respTypeEnum == null) {
                logger.debug("报文响应命令类型不支持,命令类型:{}", respType);
                buf = validateMsg(msg, index);
                continue;
            }
            switch (respTypeEnum) {
                case WRITE_DEVICE_ID:
                case WRITE_IP_PORT:
                case WRITE_METER_TYPE:
                case WRITE_WAITING_TIME:
                case WRITE_START_TIME:
                case WRITE_INTERVAL_TIME:
                case WRITE_PROTOCOL_PRIORITY:
                case RESET_DEVICE:
                case RESET_FACTORY_INIT:
                case ADD_METER_ID:
                case DEL_METER_ID:
                case CLEAR_METER_ID:
                case CONFIRM_RECEIVE:
                    result.add(parseCmdResp(buf));
                    break;
                case OPEN_VALVE:
                case CLOSE_VALVE:
                    result.add(parseCtrl(buf));
                    break;
                case READ_DEVICE_ID:
                    result.add(parseDeviceId(buf));
                    break;
                case READ_IP_PORT:
                    result.add(parseMasterIpPort(buf));
                    break;
                case READ_PARAMS:
                    result.add(parseMoreParams(buf));
                    break;
                case READ_METER_ID:
                    result.add(parseMeterInfo(buf));
                    break;
                case HEARTBEAT:
                    result.add(parseHeartBeatMsg(buf));
                    break;
                case REPORT_METER_READING_DATA:
                    result.add(parseReport(buf));
                    break;
            }
            buf = validateMsg(msg, index);

        }

        return result;
    }

    private static List<MeterInfo> parseMeterInfo(byte[] msg) {

        int size = (msg.length - 21) / 8;
        List<MeterInfo> meterInfoList = new ArrayList<>(size);
        if (size == 0) {
            return meterInfoList;
        }
        int index = 18;
        String meterId;
        while (index < msg.length - 3) {
            MeterInfo meterInfo = new MeterInfo();
            meterId = ProtocolUtils.getStringByBCD(msg, index, 7);
            meterId = ProtocolUtils.reverse(meterId, 14);
            meterInfo.setMeterId(meterId);
            index += 7;
            meterInfo.setProtocol(String.format("%02X", msg[index]));
            index++;
            meterInfoList.add(meterInfo);
        }
        return meterInfoList;
    }

    private static MoreParams parseMoreParams(byte[] msg) {
        if (msg.length < 43) {
            return null;
        }
        int index = 18;
        MoreParams params = new MoreParams();
        params.setHeartBeatIntervalTime(ProtocolUtils.byteToShort(msg[index], msg[index + 1]));
        index += 2;
        params.setMeterReadIntervalTime(ProtocolUtils.byteToShort(msg[index], msg[index + 1]));
        index += 2;
        params.setNetworkMode((short) ProtocolUtils.Unsignd(msg[index]));
        index++;
        String currentTime = ProtocolUtils.getStringByBCD(msg, index, 6);
        params.setCurrentTime(DateUtil.StrToDate(currentTime, DateUtil.TIME_LONG_12));
        index += 6;
        params.setVersion(ProtocolUtils.ascii2String(msg, index, 9));
        index += 9;
        params.setMeterType((short) ProtocolUtils.Unsignd(msg[index]));
        index++;
        params.setMeterReadWaitTime((short) ProtocolUtils.Unsignd(msg[index]));
        index++;
        params.setMeterReadStartTime(Short.parseShort(String.format("%02X", msg[index]), 10));
        return params;
    }

    private static String parseDeviceId(byte[] msg) {
        if (msg.length < 32) {
            return null;
        }
        return ProtocolUtils.ascii2String(msg, 18, 11);
    }

    private static MasterIpPort parseMasterIpPort(byte[] msg) {
        if (msg.length < 24) {
            return null;
        }
        String ipPort = ProtocolUtils.ascii2String(msg, 18, msg.length - 21);
        if (ipPort.indexOf(",") < 0) {
            return null;
        }
        MasterIpPort masterIpPort = new MasterIpPort();
        String[] arr = ipPort.split(",");
        masterIpPort.setIp(arr[0]);
        masterIpPort.setPort(Integer.parseInt(arr[1]));
        return masterIpPort;
    }


    private static CtrlResp parseCtrl(byte[] msg) {
        if (msg.length < 30) {
            return null;
        }
        CtrlResp resp = new CtrlResp();
        String commAddr = ProtocolUtils.getStringByBCD(msg, 18, 7);
        commAddr = ProtocolUtils.reverse(commAddr, 14);
        resp.setMeterId(commAddr);
        short valveStatus = (short) (msg[25] & 0x7f);
        resp.setValveStatus(valveStatus);

        String result = String.format("%02X", msg[26]);
        resp.setSuccess(result.equalsIgnoreCase("FF"));
        return resp;
    }

    private static boolean parseCmdResp(byte[] msg) {
        if (msg.length < 20) {
            return false;
        }
        String result = String.format("%02X%02X", msg[18], msg[19]);
        return result.equalsIgnoreCase("F000");
    }

    private static DataWaterMeterCollector parseHeartBeatMsg(byte[] msg) {
        DataWaterMeterCollector wmc = new DataWaterMeterCollector();
        int index = 18;
        wmc.setCimi(ProtocolUtils.ascii2String(msg, index, 15));
        index += 15;
        wmc.setIccid(ProtocolUtils.ascii2String(msg, index, 20));
        index += 20;
        wmc.setOperator((short) ProtocolUtils.Unsignd(msg[index]));
        index++;
        index++;//预留
        wmc.setSignalQuality((short) ProtocolUtils.Unsignd(msg[index]));
        index++;
        wmc.setVoltage(ProtocolUtils.byteToFloat(msg[index], msg[index + 1], 3));
        index += 2;
        wmc.setMeterQuantity((short) ProtocolUtils.Unsignd(msg[index]));
        index++;
        index += 2; //预留
        wmc.setDeviceFaultCode(String.format("%02X", msg[index]));
        index++;
        wmc.setMbusFaultCode(String.format("%02X", msg[index]));
        index++;
        index++;//预留
        wmc.setImei(ProtocolUtils.ascii2String(msg, index, 15));
        return wmc;
    }

    public static List<Object> parseReport(byte[] msg) {
        List<Object> saveDataList = new ArrayList<>();
        int index = 18;
        while (index + 20 < msg.length) {
            String dataTime = ProtocolUtils.getStringByBCD(msg, index, 6);
            index += 6;
            String commAddr = ProtocolUtils.getStringByBCD(msg, index, 7);
            commAddr = ProtocolUtils.reverse(commAddr, 14);
            index += 7;
            Date readTime = DateUtil.StrToDate(dataTime, DateUtil.TIME_LONG_12);
            String value = ProtocolUtils.getStringByBCD(msg, index, 4);
            index += 4;
            value = ProtocolUtils.reverse(value, 8);

            BigDecimal decimal = BigDecimal.valueOf(Integer.parseInt(value) * 1.0 / 100);
            saveDataList.add(buildCumulant(commAddr, readTime, decimal));

            DataWaterExtend extend = new DataWaterExtend();
            extend.setMeterStatus((short) ProtocolUtils.Unsignd(msg[index]));
            index++;

            PointPowerStatus powerStatus = new PointPowerStatus();
            powerStatus.setPowerStatus((short) ProtocolUtils.Unsignd((byte) (msg[index] & 0x7f)));
            saveDataList.add(powerStatus);
            index++;
            extend.setMeterReadingStatus(String.format("%02X", msg[index]));
            saveDataList.add(extend);
            index++;
//        float voltage = ProtocolUtils.byteToFloat(msg[index], msg[index + 1], 3);
//        extend.setVoltage(voltage);
//
//        if(voltage > 0 && voltage < 3.1) {
//            cumulant.setBatteryStatus(BatteryStatusEnum.LESS_PRESSURE.getShortValue());
//        } else {
//            cumulant.setBatteryStatus(BatteryStatusEnum.NORMAL.getShortValue());
//        }
        }
        return saveDataList;
    }

    private static DataCumulant buildCumulant(String commAddr, Date dataTime, BigDecimal value) {
        DataCumulant cumulant = new DataCumulant();
        cumulant.setCommAddr(commAddr);
        cumulant.setDataTime(dataTime);
        cumulant.setValue(value);
        return cumulant;
    }
}
